#include "irc.h"
//#include "buffer.h"

/* irc_connect takes RemoteInfo as an argument,
 * returns connected socket on success, -1 otherwise
 * all necessary data types are saved in RemoteInfo argument
 */

//char ircbuffer[IRCMAXSZ * 2];
int irc_connect(ServerInfo *irc) {
	struct addrinfo *res;
	struct addrinfo hint;
	union {
		struct sockaddr *sa;
		struct sockaddr_in *sin;
		struct sockaddr_in6 *sin6;
	} saddr;
	char str_addr[INET6_ADDRSTRLEN];
	memset(str_addr, 0, sizeof(str_addr));
	memset(&hint, 0, sizeof(hint));
	hint.ai_family = AF_UNSPEC;
	hint.ai_protocol = IPPROTO_TCP;
	hint.ai_socktype = SOCK_STREAM;
	if (getaddrinfo(irc->host, irc->port, &hint, &irc->res) != 0) {
		perror("getaddrinfo() failed");
		return -1;
	}
	for (res = irc->res; res != NULL; res = res->ai_next) {
		saddr.sa = res->ai_addr;
		switch (res->ai_family) {
		case AF_INET:
			inet_ntop(res->ai_family, &saddr.sin->sin_addr, str_addr,
					sizeof(str_addr));
			break;
		case AF_INET6:
			inet_ntop(res->ai_family, &saddr.sin6->sin6_addr, str_addr,
					sizeof(str_addr));
			break;
		default:
			printf("Unsupported address family.\n");
			break;
		}
		printf("Trying %s ..\n", str_addr);
		if ((irc->socket = socket(res->ai_family, res->ai_socktype,
				res->ai_protocol)) == -1) {
			/* socket creation failed */
			perror("Socket creation failed");
			continue;
		}
		if (connect(irc->socket, res->ai_addr, res->ai_addrlen) == 0) {
			printf("Connection established.\n");
			return irc->socket;
		} else {
			printf("Connection to %s failed: %s\n", str_addr, strerror(errno));
			continue;
		}
	}
	return -1;
}

/* register connection to the irc server */
int irc_register(ServerInfo *irc, ClientInfo *user) {
	char msgbuf[512 + 1];

	memset(&msgbuf, 0, sizeof(msgbuf));
	snprintf(msgbuf, sizeof(msgbuf), "NICK %s\n", user->nick);
	irc_send(irc, msgbuf);

	memset(&msgbuf, 0, sizeof(msgbuf));
	snprintf(msgbuf, sizeof(msgbuf), "USER %s 0 * :%s\n", user->ident,
			user->realname);
	irc_send(irc, msgbuf);

	return 0;
}

/* irc_send() needs fixing to handle newline at the end of messages
 * decide later how it's meant to be used
 */
int irc_send(ServerInfo *irc, char *msg) {
	if (sendto(irc->socket, msg, strlen(msg), MSG_EOR, irc->res->ai_addr,
			irc->res->ai_addrlen) != strlen(msg)) {
		perror("Failed to send full command buffer");
		return 1;
	} else {
		printf("[%d] SENT: %s", (int) time(NULL), msg);
	}
	return 0;
}

/* default message handlers for now, fix later */
int irc_msg_default(ServerInfo *irc, char *msg) {
	if (strstr(msg, "PING") != NULL) {
		irc_send(irc, "PONG ircbot!botident@127.0.0.1\n");
		return 1;
	}
	return 0;
}

int irc_handler_add(Node *root, char *command, void *handler) {
	Node leaf;
	memset(&leaf, 0, sizeof(leaf));
	leaf.command = command;
	leaf.handler = handler;
	addNode(root, &leaf) == 0 ?
			printf("Node added\n") : printf("Node addition failed\n");
	return 0;
}

void * irc_handler_get(Node *root, char *command) {
	Node *leaf;
	void *fp = NULL;
	printf("[%d] finding handler..\n", (int) time(NULL));
	leaf = findNode(root, command);
	if (leaf != NULL) {
		fp = leaf->handler;
	}
	return fp;
}

IRCMessage * irc_parsemsg(char * raw) {
	char *c;
	IRCMessage *msg;
	msg = calloc(1, sizeof(IRCMessage));
	/* does not check if calloc() succeeds */
	for (c = raw; *c != 0x20; c++)
		;
//	printf("Hello");
	switch (raw[0]) {
	case ':':
		msg->prefix = calloc(1, c - raw);
		if (msg->prefix == NULL)
			break;
		strncpy(msg->prefix, raw, c - raw);
		c++;
		raw += c - raw;
		for (c = raw; *c != 0x20; c++)
			;
		msg->command = calloc(1, c - raw);
		if (msg->command == NULL)
			break;
		strncpy(msg->command, raw, c - raw);
		break;
	default:
		msg->prefix = NULL;
		msg->command = calloc(1, c - raw);
		if (msg->command == NULL)
			break;
		strncpy(msg->command, raw, c - raw);
		break;
	}
	msg->params = calloc(strlen(raw) - (c - raw), 1);
	if (msg->params != NULL)
		strncpy(msg->params, &raw[c - raw + 1], strlen(raw) - (c - raw));
	return msg;
}

void * irc_getMessage(ServerInfo *irc) {
	char *rawmsg = irc_raw(irc);
	if(rawmsg == NULL){
		printf("irc_raw() failed!\n");
		return NULL;
	}
	IRCMessage *msg = irc_parsemsg(rawmsg);
	return msg;
}
char * irc_raw(ServerInfo *irc) {
	char rawbuffer[IRCMAXSZ];
	char *msgptr;
	char *delim = "\x0D\x0A";
	char *stringp = rawbuffer;

	memset(rawbuffer, 0, IRCMAXSZ);
	//	strncpy(rawbuffer, ircbuffer, IRCMAXSZ);
	strncpy(rawbuffer, irc->buffer, IRCMAXSZ);
	msgptr = strsep(&stringp, delim);

	if (stringp == NULL) {
		//		if (read(irc->socket, ircbuffer + strnlen(ircbuffer, IRCMAXSZ),
		if (read(irc->socket, irc->buffer + strnlen(irc->buffer, IRCMAXSZ),
		IRCMAXSZ) > 0)
			return irc_raw(irc);
	} else {
//		printf("RAR %d\n", (int) strnlen(msgptr, IRCMAXSZ));
// \x0D , and probably \x0A byte
		if (strnlen(msgptr, IRCMAXSZ) > 2) {
			//	memmove(ircbuffer, ircbuffer + strnlen(msgptr, IRCMAXSZ) + 2,
			memmove(irc->buffer, irc->buffer + strnlen(msgptr, IRCMAXSZ) + 2,
					(IRCMAXSZ * 2) - (strnlen(msgptr, IRCMAXSZ)));
		} else {
			msgptr = NULL;
		}
		return msgptr;
	}
	return NULL;
}
